<?php

declare(strict_types=1);

namespace MultiWorker\Worker;

use MultiWorker\Process;
use MultiWorker\Constract\WorkerHandler;
use MultiWorker\Signal;

class Worker extends Process implements WorkerHandler
{
    /**
     * 进程是否退出
     *
     * @var boolean
     */
    protected $isExit = false;

    /**
     * 接收到的信号
     *
     * @var integer
     */
    protected $signal;

    /**
     * worker id.
     *
     * @var integer
     */
    protected $workerId;

    /**
     * Worker Constructor.
     *
     * @param integer $workerId
     * @param mixed $handler
     * @param array $config
     * @return void
     */
    public function __construct (int $workerId,$handler,array $config = [])
    {
        parent::__construct($config);
        $this->handler  = $handler;
        $this->workerId = $workerId;
    }

    /**
     * worker handle.
     *
     * @return void
     */
    public function process (): void
    {
        while (true) {
            // signal trigger.
            $this->signal = intval($this->getPipe()->read());
            if ($this->signal) {
                $this->triggerSignal();
            }
            // 超出内存使用限制
            if ($this->memoryExceeded()) {
                $this->reload();
            }
            // exit
            if ($this->isExit) {
                $this->exit();
            }
            // execute handler.
            $this->execute($this->handler);
            // loop wait time.
            usleep($this->loopMicrotime);
        }
    }

    /**
     * 执行handler.
     *
     * @return void
     */
    protected function execute (): void
    {
        $handler = $this->handler;
        if (is_callable($handler)) {
            call_user_func($handler,$this);
            return;
        }
        
        if (is_string($handler)) {
            $handler = explode('@',$handler);
        }

        if (is_array($handler)) {
            $instance = is_object($handler[0]) ? $handler[0] : new $handler[0]();
            $method   = $handler[1];
        }

        call_user_func_array([$instance,$method],[$this]);
    }

    /**
     * trigger signal handler.
     *
     * @return void
     */
    protected function triggerSignal (): void
    {
        switch ($this->signal) {
            case Signal::STATUS_RELOAD:
                $this->reload();
                break;
            case Signal::STATUS_STOP:
                $this->terminate();
                break;
        }
    }
    
    /**
     * 超出内存限制判断
     *
     * @return bool
     */
    protected function memoryExceeded (): bool
    {
        return $this->maxMemoryLimit != -1 && (memory_get_usage() / 1024 / 1024) >= $this->maxMemoryLimit;
    }

    /**
     * set exit.
     *
     * @param boolean $isExit
     * @return self
     */
    public function setExit (bool $isExit = false): self
    {
        $this->isExit = $isExit;
        return $this;
    }
    /**
     * 退出进程.
     *
     * @return void
     */
    public function exit (): void
    {
        if (!$this->isExit) {
            return;
        }
        $this->getPipe()->clear();
        exit($this->signal);
    }

    /**
     * 重载进程
     *
     * @return void
     */
    public function reload (): void
    {
        $this->signal = Signal::STATUS_RELOAD;
        $this->setExit(true);
    }

    /**
     * 终止进程
     *
     * @return void
     */
    public function terminate (): void
    {
        $this->signal = Signal::STATUS_STOP;
        $this->setExit(true);
    }

    /**
     * get worker id.
     *
     * @return integer
     */
    public function getWorkerId (): int
    {
        return $this->workerId;
    }
}